package model;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import serverMessage.ServerMessage;

import database.DbFile;
import database.DbFileManager;
import database.DbFolder;
import database.DbFolderManager;
import database.User;
import database.UserManager;

/**
 * class DbAdapter - singleton - provides API for the DB
 */
public class DbAdapter {
	
	//the singleton Instance
	private static DbAdapter dbAdapter = new DbAdapter();
	
	//help classes for manage the DB tables
	private UserManager userManager;
	private DbFileManager dbFileManager;
	private DbFolderManager dbFolderManager;
	
	//private ctor
	private DbAdapter(){
		
		File f = new File("C:\\clever dropbox server\\hibernate.cfg.xml");
		//File f = new File(".\\src\\hibernate.cfg.xml");
		SessionFactory sessionFactory = new Configuration().configure(f).buildSessionFactory();
		Session session = sessionFactory.openSession();
		
		userManager = new UserManager(session);
		dbFileManager = new DbFileManager(session);
		dbFolderManager = new DbFolderManager(session);
	}
	
	/**
	 * @return an instance of RequestsManager class (singleton)
	 */
	public static DbAdapter getInstance()
	{
		return dbAdapter;	
	}
	
	/**
	 * @param userID - the user name
	 * @param password - the user's password
	 * @return 0 if failed, else if succeed
	 */
	public Integer addUser(String userID,String password)
	{
		//the is such user
		if (userManager.getUser(userID)!=null)
			return 0;
		
		//add the user and his dropbox root folder 
		int folderID = dbFolderManager.addNewDbFolder(userID,"dropbox",true,null);
		User user = new User(userID,password,folderID);
		userManager.saveUser(user);
		return 1;	 
	}
	
	/**
	 * add the file to the DB
	 * @param file - a file
	 * @return 1
	 */
	public Integer addDbFile(DbFile file)
	{
		dbFileManager.saveDbFile(file);
		return 1;
	}
	
	/**
	 * add the folder to the DB
	 * @param userID - the user name
	 * @param folderName - the folder name
	 * @return the id that the folder got. -1 if failed.
	 */
	public Integer getDbFolderID(String userID, String folderName)
	{
		
		DbFolder folder = dbFolderManager.getDbFolder(userID, folderName);
		if (folder!=null)
			return folder.getFolderID();
		
		return -1; 
	}
	
	/**
	 * @param userID - the user name
	 * @return the id of the user dropbox root folder. -1 if failed.
	 */
	public Integer getUserDropboxFolderID(String userID)
	{
		User user = userManager.getUser(userID);
		
		if (user==null)
		{
			System.out.println("no such user");
			return -1;
		}
		else
		{
			return user.getFolderID();
		}
		
	}
	
	/**
	 * @param fileName - a file name
	 * @param folderID - a folder id
	 * @return the file with that name under that folder
	 */
	public DbFile getDbFile(String fileName, Integer folderID)
	{
		return dbFileManager.getDbFile(fileName, folderID);
	}
	
	/**
	 * delete the file
	 * @param file - a file
	 */
	public void deleteDbFile(DbFile file)
	{
		dbFileManager.deleteDbFile(file);		
	}
	
	/**
	 * update the file
	 * @param file - a file
	 */
	public void updateDbFile(DbFile file)
	{
		dbFileManager.updateDbFile(file);		
	}
	
	/**
	 * @param userID - a user name
	 * @param folderName - a folder name
	 * @return add this folder to the user dropbox - return the id that the folder get. -1 if failed.
	 */
	public int addNewDbFolder(String userID,String folderName)
	{
		//if there is no such user
		if (userManager.getUser(userID)==null)
		{
			System.out.println("no such user");
			return 0;
		}
		
		//if the user already have folder with that name
		if (dbFolderManager.getDbFolder(userID, folderName)!=null)
		{
			System.out.println("user already have folder with that name");
			return 0;
		}
		
		//add the folder
		synchronized (this) {
			dbFolderManager.addNewDbFolder(userID,folderName,false,null);
		}
		
		return 1;
	}
	
	/**
	 * delete this folder from the user dropbox
	 * @param userID - a user name
	 * @param folderName - a folder name
	 * @return 0 if failed, 1 if succeed.
	 */
	public int deleteDbFolder(String userID,String folderName)
	{
		DbFolder folder = dbFolderManager.getDbFolder(userID, folderName);
		
		//check if there is such folder
		if (folder!=null)
		{
			synchronized (this) {
				int isShared = dbFolderManager.deleteDbFolder(folder);
			
				if (isShared==0) //if it's not shared folder delete it's files
				{				
					System.out.println("delete files in deleted folder");
					dbFileManager.deleteFilesInFolder(folder.getFolderID());
				}
			}
			
			return 1;
		}
		
		return 0;
	}
	
	
	/**
	 * @param userID - user name
	 * @return a list of the user's folders
	 */
	public List<DbFolder> getUserDropboxFolders(String userID)
	{		
		return dbFolderManager.getUserFolders(userID);		
	}
	
	/**
	 * 
	 * @param userID - user name
	 * @return a list of the user's files that directly under the dropbox root
	 */	
	public List<DbFile> getUserDropboxFiles(String userID)
	{
		User user = userManager.getUser(userID);
		if (user==null)
			return null;
		
		return dbFileManager.getFilesFromFolderID(user.getFolderID());
	}
	
	/**
	 * @param userID - the user name
	 * @param folderName - the folder name
	 * @return a list of the user's files that under that folder
	 */
	public List<DbFile> getFilesInUserFolderName(String userID, String folderName)
	{
		DbFolder folder = dbFolderManager.getDbFolder(userID, folderName);
		if (folder==null)
			return null;
		
		return dbFileManager.getFilesFromFolderID(folder.getFolderID());
	}
	
	/**
	 * @param userID - the user name
	 * @return the user's details
	 */
	public User getUser(String userID)
	{		
		return userManager.getUser(userID);		
	}
	
	
	/**
	 * @param fromUserID - a user name that want to share a folder
	 * @param folderName - the folder that the user want to share
	 * @param toUserID - the user name that the user want to share with
	 * @return 0 if failed, 1 if succeed.
	 */
	public Integer shareFolder(String fromUserID, String folderName, String toUserID)
	{
		System.out.println("share folder "+ folderName);
		System.out.println("users "+ toUserID + " " + fromUserID);
		
		//if there is no such user
		if ((userManager.getUser(toUserID)==null)||(userManager.getUser(fromUserID)==null))
		{
			System.out.println("no such user!!");
			return 0;
		}
		
		Integer folderID = getDbFolderID(fromUserID, folderName);
		
		//if there is no such folder
		if (folderID==-1)
		{
			System.out.println("user don't have such folder");
			return 0;
		}
		
		//if the other user already have this folder
		if (getFolderName(toUserID, folderID)!=null)
		{
			System.out.println("user already have this shared folder");
			return 0;
		}
		
		
		//create the folder in the other user dropbox 
		Integer result = dbFolderManager.addNewDbFolder(toUserID,folderName,false,folderID);
		
		System.out.println("result" + result);
		Integer i=new Integer(0);
		
		//(if there is a folder with that name create the new folder ass folderName(i))
		if (result.equals(0))
		{
			while (result.equals(0))
			{
				i = i+1;
				result = dbFolderManager.addNewDbFolder(toUserID,folderName+"("+i+")",false,folderID);
				System.out.println("result" + result);
			}
			
			folderName = folderName + "(" + i + ")";
			
		}
		
		System.out.println("create folder message");
		
		//message list for the other user - to add this folder and the files that under it
		LinkedList<ServerMessage> messagesList = new LinkedList<ServerMessage>();
		
		ServerMessage message = new ServerMessage(toUserID, "create_folder", folderID.toString(), null, null);
		messagesList.add(message);
		
		
		//get the files that under this folder and add messages about the existing files
		List<DbFile> list = dbFileManager.getFilesFromFolderID(folderID);
		Iterator<DbFile> iterator = list.iterator();
		DbFile file;
		byte[] byteFile;
		String fileName;			
		
		while (iterator.hasNext())
		{
			System.out.println("filed in shared folder");
			file = iterator.next();
			System.out.println(file);
			fileName = file.getFileName();
			byteFile = convFileToByte(new File(".\\files\\"+file.getFileID()+fileName.substring(fileName.lastIndexOf("."))));
			message = new ServerMessage(toUserID, "create_file", folderID.toString(), fileName, byteFile);
			
			messagesList.add(message);
		}
		
		//send the messages
		UsersMessages.getInstance().addMessage(toUserID, messagesList);	
		
		return 1;
	}	
	
	/**
	 * @param sharedWithUserID - a user name
	 * @param folderID - a folder id
	 * @return a list of user names that shared with him this folder (without himself) 
	 */
	public List<String> usersSharedFolder(String sharedWithUserID, Integer folderID)
	{
		return dbFolderManager.usersSharedFolder(sharedWithUserID, folderID);		
	}
	
	/**
	 * @param userID - a user name
	 * @param folderID - a folder id 
	 * @return the folder name
	 */
	public String getFolderName(String userID, Integer folderID)
	{
		DbFolder  folder = dbFolderManager.getDbFolder(userID, folderID);
		if (folder==null)
			return null;
		
		return folder.getFolderName();
		
	}
	
	//convert file to bytes
	 private byte[] convFileToByte(File file){
		 FileInputStream fis = null;
		 try {
			 fis = new FileInputStream(file);
		 } catch (FileNotFoundException e1) {
			 // TODO Auto-generated catch block
			 e1.printStackTrace();
		 }
		 
		 ByteArrayOutputStream bos = new ByteArrayOutputStream();
		 byte[] buf = new byte[1024];
		 try {
			 for (int readNum; (readNum = fis.read(buf)) != -1;) {
				 bos.write(buf, 0, readNum); //no doubt here is 0
			 }
		 } catch (IOException ex) {}
		 
		 byte[] bytes = bos.toByteArray();
		 
		 try {
			 fis.close();
			 bos.flush();
			 bos.close();
		 } catch (IOException e) {
			 // TODO Auto-generated catch block
			 e.printStackTrace();
		 }
		 
		 return bytes;
	 }	

}
